package locusts.common.entities;

import java.io.Serializable;

import javax.vecmath.Vector2d;

import locusts.lib.MathUtil;

/**
 * Hold only the intrinsic data of one instance of the most basic vehicle. Any
 * properties that are extrinsic (held by a number of entities) for example the
 * mass, shape, or friction should be held elsewhere.
 * <p>
 * 
 * @author Hamish I A Morgan
 * 
 */
public class Vehicle implements Serializable, Cloneable {
    
    private static final long serialVersionUID = 2619034965223058903L;
    
    public final Vector2d position;
    public final Vector2d velocity;
    public final Vector2d acceleration;
    
    public Vehicle(Vector2d position, Vector2d velocity, Vector2d acceleration) {
        this.position = position;
        this.velocity = velocity;
        this.acceleration = acceleration;
    }
    
    public Vehicle(double x, double y, double vx, double vy, double ax,
            double ay) {
        this(new Vector2d(x, y), new Vector2d(vx, vy), new Vector2d(ax, ay));
    }
    
    public double speed() {
        return velocity.length();
    }
    
    public double heading() {
        return Math.atan2(velocity.y, velocity.x);
    }
    
    public double bearingTo(Vector2d target) {
        
        //        
        // //
        // // TODO: may be faster to use dot product:
        
        // double ang0 = AngleMath.normalise(Math.atan2(target.y - position.y,
        // target.x - position.x)
        // - heading());
        // //
        // Vector2d a = new Vector2d(target.x - position.x, target.y -
        // position.y);
        // Vector2d b = velocity;
        //        
        double ang2 = MathUtil.angleBetween(target.x - position.x, target.y
                - position.y, velocity.x, velocity.y);
        //
        // double ang1 = AngleMath.normalise(Math.acos(a.dot(b)
        // / (a.length() * b.length())));
        // //
        // assert MathUtil.epsilonEqual(Math.abs(ang0), Math.abs(ang2), 0.1) :
        // "Angles are not equal:\n ang0 = " + ang0
        // + ", ang2 = " + ang2 + " \nfor vectors target=" + target
        // + " pos=" + position + " vel=" + velocity;
        return ang2;
    }
    
    public double bearingTo(Vehicle target) {
        return bearingTo(target.position);
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        StringBuffer buf = new StringBuffer();
        buf.append("Position:");
        buf.append(position);
        buf.append(" Velocity:");
        buf.append(velocity);
        return buf.toString();
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((position == null) ? 0 : position.hashCode());
        result = prime * result
                + ((velocity == null) ? 0 : velocity.hashCode());
        return result;
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
        try {
            if (this == obj)
                return true;
            if (getClass() != obj.getClass())
                return false;
            Vehicle other = (Vehicle) obj;
            return position.equals(other.position)
                    && velocity.equals(other.velocity);
        } catch (NullPointerException e2) {
            return false;
        } catch (ClassCastException e1) {
            return false;
        }
    }
    
    public boolean equals(Vehicle other) {
        try {
            if (this == other)
                return true;
            return position.equals(other.position)
                    && velocity.equals(other.velocity);
        } catch (NullPointerException e2) {
            return false;
        }
    }
    
    /**
     * @see java.lang.Cloneable
     */
    @Override
    public Object clone() {
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            throw new InternalError();
        }
    }
    
}